﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeanboxSoftware.BeanMap.Internal
{
  internal class MapDeclarations
  {
    internal static string MapInjectErrorMessage = "Unboxed.Maps do not support child map injection nor child map comparison";
    internal static string KeyNotFoundErrorMessage = "Key does not exist in the map [{0}]";
    internal static string InvalidKeyNotFoundErrorMessage = "Invalid KeyNotFoundBehavior was set";
    internal static string CopyToBufferNullErrorMessage = "CopyTo must be given a non null buffer";
    internal static string CopyToIndexErrorMessage = "CopyTo parameter arrayIndex was out of range";
    internal static string GenerationCannotBeSerializedErrorMessage = "This map expected to use a deserialized generation, which is not supported";
  }

  public abstract partial class MapBase<TGen, TValue, TPrimaryKey, TPrimaryValue> : DictionaryCommonBase<TPrimaryKey, TPrimaryValue>
  {
    protected MapBase()
    {
      KeyNotFoundBehavior = KeyNotFoundBehavior.ThrowKeyNotFoundException;
    }

    protected class _PassthroughFactory
    {
      public TMap CreatePassthrough<TMap>(Func<bool, IDictionary<TPrimaryKey, TPrimaryValue>> passThrough, Action removalCleanup, TGen callback)
        where TMap : MapBase<TGen, TValue, TPrimaryKey, TPrimaryValue>, new()
      {
        var map = new TMap();
        map.DefaultGeneration = callback;
        map.SetPassthrough(passThrough, removalCleanup);
        return map;
      }
    }

    public KeyNotFoundBehavior KeyNotFoundBehavior { get; set; }

    private TValue _defaultValue;
    private TGen _defaultGeneration;

    public TValue DefaultValue
    {
      set
      {
        KeyNotFoundBehavior = KeyNotFoundBehavior.DefaultValue;
        _defaultValue = value;
      }
    }

    public TGen DefaultGeneration
    {
      set
      {
        KeyNotFoundBehavior = KeyNotFoundBehavior.DefaultGeneration;
        _defaultGeneration = value;
        _faultedGeneration = false;
      }
    }

    protected TValue HandleKeyNotFound(Func<TGen, TValue> getGeneratedValue, Action<TValue> setGeneratedValue, Func<object[]> keyArrayMethod)
    {
      switch (KeyNotFoundBehavior)
      {
        case KeyNotFoundBehavior.DefaultValue:
          return _defaultValue;
        case KeyNotFoundBehavior.DefaultGeneration:

          if (_faultedGeneration)
            throw new NotSupportedException(MapDeclarations.GenerationCannotBeSerializedErrorMessage);

          var v = getGeneratedValue(_defaultGeneration);
          if (_passThrough == null) setGeneratedValue(v);
          return v;
        case KeyNotFoundBehavior.ThrowKeyNotFoundException:
          throw new KeyNotFoundException(string.Format(MapDeclarations.KeyNotFoundErrorMessage, 
            string.Join(", ", keyArrayMethod().Select(ko => ko == null ? string.Empty : ko.ToString()).ToArray())));
      }

      throw new ArgumentOutOfRangeException(MapDeclarations.InvalidKeyNotFoundErrorMessage);
    }
  }
}
